package com.kexio.file.storage.impl;

import java.io.InputStream;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.CopyObjectRequest;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import com.aliyun.oss.model.GetObjectRequest;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import com.kexio.file.config.FileStorageProperties;
import com.kexio.file.storage.StorageFileInfo;
import com.kexio.file.storage.StorageProvider;
import com.kexio.file.storage.StorageResult;

/**
 * 阿里云OSS存储提供者
 * 
 * 基于阿里云对象存储服务的文件存储实现
 * 
 * @author Kexio Team
 * @since 1.0.0
 */
@Component
@ConditionalOnClass(name = "com.aliyun.oss.OSS")
public class OSSStorageProvider implements StorageProvider {
    
    private static final Logger log = LoggerFactory.getLogger(OSSStorageProvider.class);
    
    @Autowired
    private FileStorageProperties storageProperties;
    
    private volatile OSS ossClient;
    
    /**
     * 获取OSS客户端（懒加载）
     */
    private OSS getOSSClient() {
        if (ossClient == null) {
            synchronized (this) {
                if (ossClient == null) {
                    initializeOSSClient();
                }
            }
        }
        return ossClient;
    }
    
    /**
     * 初始化OSS客户端
     */
    private void initializeOSSClient() {
        try {
            FileStorageProperties.OssStorageConfig ossConfig = storageProperties.getOss();
            
            ossClient = new OSSClientBuilder().build(
                ossConfig.getEndpoint(),
                ossConfig.getAccessKeyId(),
                ossConfig.getAccessKeySecret(),
                ossConfig.getSecurityToken()
            );
            
            log.info("阿里云OSS客户端初始化成功: endpoint={}", ossConfig.getEndpoint());
            
        } catch (Exception e) {
            log.error("阿里云OSS客户端初始化失败", e);
            throw new RuntimeException("阿里云OSS客户端初始化失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public String getStorageType() {
        return "oss";
    }
    
    @Override
    public StorageResult storeFile(String bucketName, String objectKey, InputStream inputStream,
                                  long contentLength, String contentType, Map<String, String> metadata) {
        try {
            OSS client = getOSSClient();
            
            // 设置对象元数据
            ObjectMetadata objectMetadata = new ObjectMetadata();
            objectMetadata.setContentLength(contentLength);
            if (StringUtils.hasText(contentType)) {
                objectMetadata.setContentType(contentType);
            }
            if (metadata != null && !metadata.isEmpty()) {
                objectMetadata.setUserMetadata(metadata);
            }
            
            // 创建上传请求
            PutObjectRequest request = new PutObjectRequest(bucketName, objectKey, inputStream, objectMetadata);
            
            // 执行上传
            PutObjectResult result = client.putObject(request);
            
            // 构建存储结果
            StorageResult storageResult = new StorageResult();
            storageResult.setBucketName(bucketName);
            storageResult.setObjectKey(objectKey);
            storageResult.setFileSize(contentLength);
            storageResult.setContentType(contentType);
            storageResult.setEtag(result.getETag());
            storageResult.setStorageUrl(buildStorageUrl(bucketName, objectKey));
            storageResult.setMetadata(metadata);
            storageResult.setSuccess(true);
            
            log.debug("阿里云OSS文件上传成功: bucket={}, key={}, size={}, etag={}", 
                     bucketName, objectKey, contentLength, result.getETag());
            
            return storageResult;
            
        } catch (Exception e) {
            log.error("阿里云OSS文件上传失败: bucket={}, key={}", bucketName, objectKey, e);
            throw new RuntimeException("阿里云OSS文件上传失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public InputStream getFile(String bucketName, String objectKey) {
        try {
            OSS client = getOSSClient();
            
            OSSObject ossObject = client.getObject(new GetObjectRequest(bucketName, objectKey));
            return ossObject.getObjectContent();
            
        } catch (Exception e) {
            log.error("阿里云OSS文件获取失败: bucket={}, key={}", bucketName, objectKey, e);
            throw new RuntimeException("阿里云OSS文件获取失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public boolean deleteFile(String bucketName, String objectKey) {
        try {
            OSS client = getOSSClient();
            
            client.deleteObject(bucketName, objectKey);
            
            log.debug("阿里云OSS文件删除成功: bucket={}, key={}", bucketName, objectKey);
            return true;
            
        } catch (Exception e) {
            log.error("阿里云OSS文件删除失败: bucket={}, key={}", bucketName, objectKey, e);
            return false;
        }
    }
    
    @Override
    public boolean fileExists(String bucketName, String objectKey) {
        try {
            OSS client = getOSSClient();
            
            return client.doesObjectExist(bucketName, objectKey);
            
        } catch (Exception e) {
            log.error("阿里云OSS文件存在性检查失败: bucket={}, key={}", bucketName, objectKey, e);
            return false;
        }
    }
    
    @Override
    public StorageFileInfo getFileInfo(String bucketName, String objectKey) {
        try {
            OSS client = getOSSClient();
            
            ObjectMetadata metadata = client.getObjectMetadata(bucketName, objectKey);
            
            StorageFileInfo fileInfo = new StorageFileInfo();
            fileInfo.setFileSize(metadata.getContentLength());
            fileInfo.setContentType(metadata.getContentType());
            fileInfo.setLastModified(toLocalDateTime(metadata.getLastModified()));
            fileInfo.setEtag(metadata.getETag());
            fileInfo.setMetadata(metadata.getUserMetadata());
            fileInfo.setExists(true);
            
            return fileInfo;
            
        } catch (Exception e) {
            log.error("阿里云OSS文件信息获取失败: bucket={}, key={}", bucketName, objectKey, e);
            throw new RuntimeException("阿里云OSS文件信息获取失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public String generatePreSignedUploadUrl(String bucketName, String objectKey,
                                           int expireMinutes, String contentType) {
        try {
            OSS client = getOSSClient();
            
            Date expiration = new Date(System.currentTimeMillis() + expireMinutes * 60 * 1000L);
            
            GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(
                bucketName, objectKey, com.aliyun.oss.HttpMethod.PUT);
            request.setExpiration(expiration);
            
            if (StringUtils.hasText(contentType)) {
                request.setContentType(contentType);
            }
            
            URL url = client.generatePresignedUrl(request);
            return url.toString();
            
        } catch (Exception e) {
            log.error("阿里云OSS预签名上传URL生成失败: bucket={}, key={}", bucketName, objectKey, e);
            throw new RuntimeException("阿里云OSS预签名上传URL生成失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public String generatePreSignedDownloadUrl(String bucketName, String objectKey, int expireMinutes) {
        try {
            OSS client = getOSSClient();
            
            Date expiration = new Date(System.currentTimeMillis() + expireMinutes * 60 * 1000L);
            
            GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(
                bucketName, objectKey, com.aliyun.oss.HttpMethod.GET);
            request.setExpiration(expiration);
            
            URL url = client.generatePresignedUrl(request);
            return url.toString();
            
        } catch (Exception e) {
            log.error("阿里云OSS预签名下载URL生成失败: bucket={}, key={}", bucketName, objectKey, e);
            throw new RuntimeException("阿里云OSS预签名下载URL生成失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public boolean copyFile(String sourceBucket, String sourceKey, String destBucket, String destKey) {
        try {
            OSS client = getOSSClient();
            
            CopyObjectRequest copyRequest = new CopyObjectRequest(sourceBucket, sourceKey, destBucket, destKey);
            client.copyObject(copyRequest);
            
            log.debug("阿里云OSS文件复制成功: {}:{} -> {}:{}", sourceBucket, sourceKey, destBucket, destKey);
            return true;
            
        } catch (Exception e) {
            log.error("阿里云OSS文件复制失败: {}:{} -> {}:{}", sourceBucket, sourceKey, destBucket, destKey, e);
            return false;
        }
    }
    
    @Override
    public boolean createBucketIfNotExists(String bucketName) {
        try {
            OSS client = getOSSClient();
            
            // 检查存储桶是否存在
            if (!client.doesBucketExist(bucketName)) {
                // 创建存储桶
                client.createBucket(bucketName);
                log.info("阿里云OSS存储桶创建成功: {}", bucketName);
            } else {
                log.debug("阿里云OSS存储桶已存在: {}", bucketName);
            }
            
            return true;
            
        } catch (Exception e) {
            log.error("阿里云OSS存储桶创建失败: {}", bucketName, e);
            return false;
        }
    }
    
    @Override
    public boolean verifyFileIntegrity(String bucketName, String objectKey, String expectedHash) {
        try {
            ObjectMetadata metadata = getOSSClient().getObjectMetadata(bucketName, objectKey);
            String actualEtag = metadata.getETag();
            
            // OSS的ETag通常是文件的MD5哈希值
            if (actualEtag != null) {
                actualEtag = actualEtag.replaceAll("\"", "");
            }
            
            boolean isValid = expectedHash != null && expectedHash.equals(actualEtag);
            
            log.debug("阿里云OSS文件完整性验证: bucket={}, key={}, expected={}, actual={}, valid={}", 
                     bucketName, objectKey, expectedHash, actualEtag, isValid);
                     
            return isValid;
            
        } catch (Exception e) {
            log.error("阿里云OSS文件完整性验证失败: bucket={}, key={}", bucketName, objectKey, e);
            return false;
        }
    }
    
    /**
     * 构建存储URL
     */
    private String buildStorageUrl(String bucketName, String objectKey) {
        FileStorageProperties.OssStorageConfig ossConfig = storageProperties.getOss();
        String endpoint = ossConfig.getEndpoint();
        
        // 构建公网访问URL
        // 格式：https://bucket-name.endpoint/object-key
        if (endpoint.startsWith("http://") || endpoint.startsWith("https://")) {
            String protocol = endpoint.startsWith("https://") ? "https://" : "http://";
            String domain = endpoint.substring(protocol.length());
            return protocol + bucketName + "." + domain + "/" + objectKey;
        } else {
            return "https://" + bucketName + "." + endpoint + "/" + objectKey;
        }
    }
    
    /**
     * Date转LocalDateTime
     */
    private LocalDateTime toLocalDateTime(Date date) {
        if (date == null) {
            return null;
        }
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }
}
